Run Decorator Code

Important for Decorators:
The decorator code itself runs when the decorator is APPLIED to the decorated function,
rather than when the decorated function is called!!!

Example 1

registry = []

def register(decorated):
    ''' Decorator '''
    registry.append(decorated)
    return decorated

# Any function that receives the @register decorator
# will have itself appended to registry list
@register
def func_01(x=3):
    return x

@register
def func_02():
    return None

@register
def func_03(x=5):
    return x

func_results = []
for func in registry:
    func_results.append(func())
print(func_results)
# [3, None, 5]

# call functions
res = func_01(4)
res = func_01(8)
res = func_03(3)
res = func_02()
res = func_03(9)

func_results = []
for func in registry:
    func_results.append(func())
print(func_results)
# [3, None, 5]  - not changed!

Example 2

class Registry(object):

    def __init__(self):
        self._functions = []

    def register(self, decorated):
        self._functions.append(decorated)
        return decorated

    def run_all(self, *args, **kwargs):
        return_values = []
        for func in self._functions:
            return_values.append(func(*args, **kwargs))
        return return_values

r1 = Registry()
r2 = Registry()

@r1.register
def func_01(x=3):
    return x

@r2.register
def func_02(x=5):
    return x

@r1.register
@r2.register
def func_03(x=7):
    return x

# Running the code from either registry’s run_all method gives the following results
print(r1.run_all())
# [3, 7]
print(r2.run_all())
# [5, 7]
# Notice that the run_all method is able to take arguments,
# which it then passes to the underlying functions when they are run.
print(r1.run_all(x=4))
# [4, 4]